/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace DaveSexton.DocProject.Sandcastle.TopicManagement
{
	internal sealed partial class RegexTopicFilterControl : UserControl, ITopicFilterEditor
	{
		#region Public Properties
		[Category("Data"), DefaultValue("")]
		public string Pattern
		{
			get
			{
				return regexPatternTextBox.Text;
			}
			set
			{
				regexPatternTextBox.Text = value;
			}
		}

		[Category("Data"), DefaultValue(RegexOptions.None)]
		public RegexOptions SelectedRegexOptions
		{
			get
			{
				RegexOptions options = RegexOptions.None;

				if (reIgnoreCaseCheckBox.Checked)
					options |= RegexOptions.IgnoreCase;

				if (reIgnoreWhiteSpaceCheckBox.Checked)
					options |= RegexOptions.IgnorePatternWhitespace;

				if (reECMAScriptCheckBox.Checked)
					options |= RegexOptions.ECMAScript;

				if (reCultureInvariantCheckBox.Checked)
					options |= RegexOptions.CultureInvariant;

				if (reMultilineCheckBox.Checked)
					options |= RegexOptions.Multiline;

				if (reSingleLineCheckBox.Checked)
					options |= RegexOptions.Singleline;

				if (reRightToLeftCheckBox.Checked)
					options |= RegexOptions.RightToLeft;

				return options;
			}
			set
			{
				synchronizingRegexChecks = true;

				try
				{
					reNoneCheckBox.Checked = value == RegexOptions.None;

					reIgnoreCaseCheckBox.Checked = (value & RegexOptions.IgnoreCase) == RegexOptions.IgnoreCase;
					reIgnoreWhiteSpaceCheckBox.Checked = (value & RegexOptions.IgnorePatternWhitespace) == RegexOptions.IgnorePatternWhitespace;
					reECMAScriptCheckBox.Checked = (value & RegexOptions.ECMAScript) == RegexOptions.ECMAScript;
					reCultureInvariantCheckBox.Checked = (value & RegexOptions.CultureInvariant) == RegexOptions.CultureInvariant;
					reMultilineCheckBox.Checked = (value & RegexOptions.Multiline) == RegexOptions.Multiline;
					reSingleLineCheckBox.Checked = (value & RegexOptions.Singleline) == RegexOptions.Singleline;
					reRightToLeftCheckBox.Checked = (value & RegexOptions.RightToLeft) == RegexOptions.RightToLeft;
				}
				finally
				{
					synchronizingRegexChecks = false;
				}
			}
		}

		[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool IgnoreCase
		{
			get
			{
				return reIgnoreCaseCheckBox.Checked;
			}
			set
			{
				reIgnoreCaseCheckBox.Checked = value;
			}
		}

		[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public RegexTopicFilter Filter
		{
			get
			{
				return boundFilter ?? new RegexTopicFilter(Pattern, SelectedRegexOptions);
			}
		}
		#endregion

		#region Private / Protected
		private bool synchronizingRegexChecks;
		private RegexTopicFilter boundFilter;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="RegexTopicFilterControl" /> class.
		/// </summary>
		public RegexTopicFilterControl()
		{
			InitializeComponent();

			((Bitmap) regexQuickHelpPicture.Image).MakeTransparent(Color.Fuchsia);
			((Bitmap) regexExternalHelpPicture.Image).MakeTransparent(Color.Fuchsia);
		}
		#endregion

		#region Methods
		public void LoadFilter(RegexTopicFilter filter)
		{
			if (filter == null)
				throw new ArgumentNullException("filter");
			else if (boundFilter != null)
				throw new InvalidOperationException(Resources.Errors.RegexTopicFilterAlreadyBound);

			SelectedRegexOptions = filter.Options;
			regexPatternTextBox.Text = filter.Pattern;
		}

		public void BindFilter(RegexTopicFilter filter)
		{
			if (filter == null)
			{
				DataBindings.Clear();
				regexPatternTextBox.DataBindings.Clear();

				SelectedRegexOptions = RegexOptions.None;
				regexPatternTextBox.Clear();

				boundFilter = null;
			}
			else
			{
				DataBindings.Add("SelectedRegexOptions", filter, "Options");
				regexPatternTextBox.DataBindings.Add("Text", filter, "Pattern");

				boundFilter = filter;
			}
		}
		#endregion

		#region Events
		private readonly object PatternChangedEvent = new object();

		/// <summary>
		/// Event raised when the <see cref="Pattern"/> property value changes.
		/// </summary>
		[Category("Property Changed")]
		[Description("Event raised when the Pattern property value changes.")]
		public event EventHandler PatternChanged
		{
			add
			{
				lock (PatternChangedEvent)
				{
					Events.AddHandler(PatternChangedEvent, value);
				}
			}
			remove
			{
				lock (PatternChangedEvent)
				{
					Events.RemoveHandler(PatternChangedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="PatternChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		private void OnPatternChanged(EventArgs e)
		{
			EventHandler handler = null;

			lock (PatternChangedEvent)
			{
				handler = (EventHandler) Events[PatternChangedEvent];
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion

		#region Event Handlers
		private void regexPatternTextBox_TextChanged(object sender, EventArgs e)
		{
			OnPatternChanged(e);

			TopicFilterEditorTypeEventArgs args = new TopicFilterEditorTypeEventArgs(this.GetType());
			OnCanApplyChanged(args);
			OnCanSaveChanged(args);
		}

		private void regexPatternTextBox_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == (char) Keys.Return)
				reIgnoreWhiteSpaceCheckBox.Checked = true;
		}

		private void reNoneCheckBox_CheckedChanged(object sender, EventArgs e)
		{
			if (!synchronizingRegexChecks)
			{
				synchronizingRegexChecks = true;

				if (reNoneCheckBox.Checked)
				{
					reIgnoreCaseCheckBox.Checked = false;
					reMultilineCheckBox.Checked = false;
					reSingleLineCheckBox.Checked = false;
					reIgnoreWhiteSpaceCheckBox.Checked = false;
					reRightToLeftCheckBox.Checked = false;
					reECMAScriptCheckBox.Checked = false;
					reCultureInvariantCheckBox.Checked = false;
				}
				else
					// user cannot uncheck None if it's the only item checked
					reNoneCheckBox.Checked = true;

				synchronizingRegexChecks = false;
			}
		}

		private void reOption_CheckedChanged(object sender, EventArgs e)
		{
			if (!synchronizingRegexChecks)
			{
				synchronizingRegexChecks = true;

				bool isItemChecked = false;

				foreach (Control control in reOptionsTableLayoutPanel.Controls)
				{
					CheckBox checkBox = control as CheckBox;

					if (checkBox != null && checkBox != reNoneCheckBox && checkBox.Checked)
					{
						isItemChecked = true;
						break;
					}
				}

				reNoneCheckBox.Checked = !isItemChecked;

				synchronizingRegexChecks = false;
			}
		}

		private void regexQuickHelpPicture_Click(object sender, EventArgs e)
		{
			regexHelpContextMenu.Show(regexQuickHelpPicture, regexQuickHelpPicture.Width, 0);
		}

		private void regexHelpContextMenu_Closing(object sender, ToolStripDropDownClosingEventArgs e)
		{
			if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked)
				e.Cancel = true;
		}

		private void regexExternalHelpPicture_Click(object sender, EventArgs e)
		{
			System.Diagnostics.Process.Start(Resources.Text.RegexHelpUrl);
		}
		#endregion

		#region ITopicFilterEditor Members
		[Browsable(false)]
		public bool CanApply
		{
			get
			{
				return regexPatternTextBox.TextLength > 0;
			}
		}

		[Browsable(false)]
 		public bool CanSave
		{
			get
			{
				return CanApply;
			}
		}

		string ITopicFilterEditor.Title
		{
			get
			{
				return Resources.Text.RegexTopicFilter;
			}
		}

		ITopicFilter ITopicFilterEditor.Filter
		{
			get
			{
				return Filter;
			}
		}

		void ITopicFilterEditor.LoadFilter(ITopicFilter filter)
		{
			LoadFilter((RegexTopicFilter) filter);
		}

		void ITopicFilterEditor.BindFilter(ITopicFilter filter)
		{
			BindFilter((RegexTopicFilter) filter);
		}

		private readonly object CanApplyChangedEvent = new object();
		private readonly object CanSaveChangedEvent = new object();

		/// <summary>
		/// Event raised when the <see cref="CanSave"/> property value changes.
		/// </summary>
		[Category("Property Changed")]
		[Description("Event raised when the CanSave property value changes.")]
		public event EventHandler<TopicFilterEditorTypeEventArgs> CanSaveChanged
		{
			add
			{
				lock (CanSaveChangedEvent)
				{
					Events.AddHandler(CanSaveChangedEvent, value);
				}
			}
			remove
			{
				lock (CanSaveChangedEvent)
				{
					Events.RemoveHandler(CanSaveChangedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="CanSaveChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="TopicFilterEditorTypeEventArgs" /> object that provides the arguments for the event.</param>
		private void OnCanSaveChanged(TopicFilterEditorTypeEventArgs e)
		{
			EventHandler<TopicFilterEditorTypeEventArgs> handler = null;

			lock (CanSaveChangedEvent)
			{
				handler = (EventHandler<TopicFilterEditorTypeEventArgs>) Events[CanSaveChangedEvent];
			}

			if (handler != null)
				handler(this, e);
		}

		/// <summary>
		/// Event raised when the <see cref="CanApply"/> property value changes.
		/// </summary>
		[Category("Property Changed")]
		[Description("Event raised when the CanApply property value changes.")]
		public event EventHandler<TopicFilterEditorTypeEventArgs> CanApplyChanged
		{
			add
			{
				lock (CanApplyChangedEvent)
				{
					Events.AddHandler(CanApplyChangedEvent, value);
				}
			}
			remove
			{
				lock (CanApplyChangedEvent)
				{
					Events.RemoveHandler(CanApplyChangedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="CanApplyChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="TopicFilterEditorTypeEventArgs" /> object that provides the arguments for the event.</param>
		private void OnCanApplyChanged(TopicFilterEditorTypeEventArgs e)
		{
			EventHandler<TopicFilterEditorTypeEventArgs> handler = null;

			lock (CanApplyChangedEvent)
			{
				handler = (EventHandler<TopicFilterEditorTypeEventArgs>) Events[CanApplyChangedEvent];
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion
	}
}
